<script setup lang="ts">
import type Node from 'element-plus/es/components/tree/src/model/node';
import type {
  TreeKey,
  TreeNodeData,
} from 'element-plus/es/components/tree/src/tree.type.mjs';

import type {
  AppPosition,
  CommonTreeProps,
  FolderProps,
  OptionProps,
} from '@/types';

import {
  type ComponentInternalInstance,
  computed,
  defineAsyncComponent,
  getCurrentInstance,
  hydrateOnVisible,
  nextTick,
  onMounted,
  onUnmounted,
  type PropType,
  reactive,
  ref,
  useTemplateRef,
  watch,
} from 'vue';

import { Icon } from '@/components/core/icon';

import { Search } from '@element-plus/icons-vue';
import {
  ElButton,
  ElButtonGroup,
  ElDropdown,
  ElDropdownItem,
  ElDropdownMenu,
  ElInput,
  ElMessage,
  ElMessageBox,
  ElTree,
  ClickOutside as vClickOutside,
} from 'element-plus';
import { customAlphabet } from 'nanoid';

import { Horizontal } from '@/components/core/resize-layout';
import { useAppStore } from '@/store';
import { formatIcon, splitChar } from '@/utils';

defineOptions({
  name: 'SystemFolder',
});
const { nowRect } = defineProps({
  nowRect: {
    default: () => ({}),
    type: Object as PropType<AppPosition>,
  },
});
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10);
const FolderGrid = defineAsyncComponent({
  hydrate: hydrateOnVisible(),
  loader: () => import('./components/folder/Grid.vue'),
});
const FolderList = defineAsyncComponent({
  hydrate: hydrateOnVisible(),
  loader: () => import('./components/folder/List.vue'),
});
const ContextMenu = defineAsyncComponent({
  hydrate: hydrateOnVisible(),
  loader: () => import('./components/folder/ContextMenu.vue'),
});
const MoveTo = defineAsyncComponent({
  hydrate: hydrateOnVisible(),
  loader: () => import('../_core/common/MoveTo.vue'),
});

const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const baseUrl = proxy?.$config.baseUrl;
const appStore = useAppStore();
const treeRef = ref<InstanceType<typeof ElTree>>();
const documentElRef = useTemplateRef('documentElRef');
const moveToRef = useTemplateRef('moveToRef');
const documentHeight = ref(0);
const loading = ref(false);
const keyword = ref('');
const defaultNodeId = nanoid();
const currentData = ref<CommonTreeProps>();
const currentPath = ref<string>(); // 当前路径
const accessHistory = ref<string[]>([splitChar]);
const accessIndex = ref(0);
const expandDictionary = ref<Record<string, string>>({}); // 展开节点字典
const breadcrumb = computed(() => {
  if (currentPath.value === splitChar) return [];
  const paths = currentPath.value?.split(splitChar).filter(Boolean) || [];
  const result: OptionProps[] = [];
  let path = '';

  paths.forEach((item) => {
    if (item) {
      path += `${splitChar}${item}`;
      result.push({ name: item, path });
    }
  });

  return result;
});
const list = ref<FolderProps[]>();
const checkedList = ref<string[]>();

const sortKey = ref('');
const sortOrder = ref('');
const layoutKey = ref('list');
const sort = reactive({
  name: 'asc',
  size: 'asc',
  time: 'asc',
  type: 'asc',
});

const layoutSize = reactive({
  lw: 0,
  mw: 0,
});

const contentRightStyle = computed(() => {
  let height = 32 + 32; // 顶部高度+导航栏高度
  if (layoutSize.mw <= 480) {
    height = 64 + 32; // 顶部高度+导航栏高度
    return {
      height: `calc(100% - ${height}px)`,
    };
  }
  return {
    height: `calc(100% - ${height}px)`,
  };
});

const contextmenuVisible = ref(false);
const contextMenuRef = useTemplateRef('contextMenuRef');
const contextmenuX = ref(0);
const contextmenuY = ref(0);
const treeData = ref<CommonTreeProps[]>([]);

const defaultProps = {
  disabled: 'disabled',
  id: 'id',
  isLeaf: 'isLeaf',
  label: 'label',
  children: 'children',
};

// 水平分隔左右宽度
const onHorizontalResize = (data: { lw: number; mw: number }) => {
  Object.assign(layoutSize, data);
};

// 列头排序
const sortBy = (key: keyof typeof sort) => {
  sort[key] = sort[key] === 'asc' ? 'desc' : 'asc';
  sortKey.value = key;
  sortOrder.value = sort[key];
};
// 排序
const setSort = (key: string) => {
  switch (key) {
    case 'asc':
    case 'desc': {
      sortOrder.value = key;
      const field = sortKey.value as keyof typeof sort;
      sort[field] = key;
      break;
    }
    default: {
      sortKey.value = key;
      break;
    }
  }
};
const sortKeyState = computed(() => (key: string) => {
  return sortKey.value === key ? `mdi:circle` : `mdi:circle-outline`;
});
const sortOrderState = computed(() => (key: string) => {
  return sortOrder.value === key ? `mdi:circle` : `mdi:circle-outline`;
});

// 设置程序方式
const setLayout = (key: string) => {
  layoutKey.value = key;
};

const layoutState = computed(() => {
  let icon = `fa6-solid:list`;
  let title = '详细信息';
  switch (layoutKey.value) {
    case 'grid-large': {
      icon = `mdi:grid-large`;
      title = '大图标';
      break;
    }
    case 'grid-middle': {
      icon = `mdi:view-grid-outline`;
      title = '中图标';
      break;
    }
    case 'grid-small': {
      icon = `mdi:grid`;
      title = '小图标';
      break;
    }
    default: {
      icon = `fa6-solid:list`;
      title = '详细信息';
      break;
    }
  }
  return {
    icon,
    title,
  };
});

// 加载节点
function reloadNode(data?: CommonTreeProps) {
  if (!data) {
    return;
  }
  loading.value = true;
  try {
    const { path } = data;
    const url = `${baseUrl}/folder/list?path=${path}`;
    fetch(url, {
      method: 'GET',
    })
      .then((response) => response.json())
      .then((res) => {
        const { code, data, msg } = res;
        if (code === 200 && data) {
          list.value = data as FolderProps[];
          list.value.map((m) => {
            m.icon = formatIcon(m);
            return m;
          });
        } else {
          ElMessage.error(msg || '获取文件夹目录失败');
        }
      });
  } catch (error) {
    console.log(error);
    ElMessage.error('请求发生了错误，请稍后重试');
  } finally {
    loading.value = false;
  }
}

// 刷新节点
const refreshTreeNode = (unids: TreeKey[] | TreeNodeData[]) => {
  unids.forEach((unid) => {
    if (unid) {
      const node = treeRef.value?.getNode(unid);
      if (node) {
        node.loaded = false; // 告诉组件这个节点需要加载
        node.expand(); // 主动调用展开节点方法，重新查询该节点下的所有子节点
      }
    }
  });
};

const saveHistory = (path: string) => {
  accessHistory.value = accessHistory.value.slice(0, accessIndex.value + 1);
  accessHistory.value.push(path);
  accessIndex.value++;
};

// 节点点击
const onNodeClick = (data: CommonTreeProps) => {
  currentData.value = data;
  const { path } = data;
  currentPath.value = path;
  saveHistory(path);
  reloadNode(data);
  contextmenuVisible.value = false;
};

const setExpandDictionary = (node: Node) => {
  if (node) {
    expandDictionary.value[node.data.path] = node.data.id;
    if (node.childNodes) {
      node.childNodes.forEach((childNode) => {
        setExpandDictionary(childNode);
      });
    }
  }
};

const delExpandDictionary = (node: Node) => {
  if (node) {
    delete expandDictionary.value[node.data.path];
    if (node.childNodes) {
      node.childNodes.forEach((childNode) => {
        delExpandDictionary(childNode);
      });
    }
  }
};
const onNodeExpand = (_data: CommonTreeProps, node: Node) => {
  setExpandDictionary(node);
  contextmenuVisible.value = false;
};

const onNodeCollaps = (_data: CommonTreeProps, node: Node) => {
  delExpandDictionary(node);
  contextmenuVisible.value = false;
};

// 历史
const onHistory = (arrow: number) => {
  if (arrow === -1 && accessIndex.value > 0) {
    accessIndex.value--;
    currentPath.value = accessHistory.value[accessIndex.value];
  } else if (
    arrow === 1 &&
    accessIndex.value < accessHistory.value.length - 1
  ) {
    accessIndex.value++;
    currentPath.value = accessHistory.value[accessIndex.value];
  }
  if (!currentPath.value) return;
  const node = {
    path: currentPath.value,
  } as CommonTreeProps;
  reloadNode(node);
};

// 返回上级目录
const onParent = () => {
  const fullPath = currentPath.value;
  const index = fullPath?.lastIndexOf(splitChar) || -1; // 找到最后一个斜杠的位置
  const pathArray = fullPath?.split(splitChar).filter(Boolean); // 按斜杠分割
  let resultPath = fullPath;
  if (index !== -1) {
    resultPath = fullPath?.slice(0, Math.max(0, index)) ?? splitChar;
  } else if (pathArray?.length === 1) {
    resultPath = splitChar; // 根目录
  }

  if (resultPath) {
    const node = {
      path: resultPath,
    } as CommonTreeProps;
    accessIndex.value--;
    accessIndex.value = Math.max(0, accessIndex.value);
    currentPath.value = resultPath;
    accessHistory.value = accessHistory.value.slice(0, accessIndex.value);
    accessHistory.value.push(resultPath);
    reloadNode(node);
  }
};

// 刷新当前路径
const onRefresh = () => {
  if (!currentPath.value) {
    currentPath.value = splitChar;
  }
  reloadNode({ path: currentPath.value } as CommonTreeProps);
  ElMessage.success({
    grouping: true,
    message: '刷新成功',
    type: 'success',
  });
};

// 新建文件夹
const onNewFolder = (path?: string) => {
  const targetPath = path || currentPath.value;
  if (!targetPath) {
    ElMessage.warning('当前路径无法新建文件夹，请重试');
    return;
  }
  ElMessageBox.prompt('请输入文件夹名称', '新建文件夹', {
    buttonSize: 'small',
    cancelButtonText: '取消',
    confirmButtonText: '确定',
    customClass: 'wos-common-confirm',
    draggable: true,
    inputErrorMessage: '无效的名称，请重新输入',
    inputPattern: /^[^"%*/:<>?\\|]+$/,
    inputPlaceholder: '请输入新的文件或文件夹名称',
    inputValue: '新建文件夹',
  })
    .then(({ value }) => {
      loading.value = true;
      try {
        const postData = { name: value, path: targetPath };
        const url = `${baseUrl}/folder/newFolder`;
        fetch(url, {
          body: JSON.stringify(postData),
          headers: {
            'Content-Type': 'application/json;charset=utf-8',
          },
          method: 'POST',
        })
          .then((response) => response.json())
          .then((res) => {
            const { code, msg } = res;
            if (code === 200) {
              const id = expandDictionary.value[targetPath as string];
              if (id) {
                refreshTreeNode([id]);
              }
              // 刷新当前文件夹
              reloadNode({ path: targetPath } as CommonTreeProps);
              ElMessage.success({
                message: msg || '新建文件夹成功',
                type: 'success',
              });
            } else {
              ElMessage.error(msg || '获取文件夹目录失败');
            }
          });
      } catch (error) {
        console.log(error);
        ElMessage.error('请求发生了错误，请稍后重试');
      } finally {
        loading.value = false;
      }
    })
    .catch(() => {
      console.log('取消');
    });
};

// 删除
const onDelete = (files?: string[], targetPath?: string) => {
  const deleteFiles = files || checkedList.value;
  const deletePath = targetPath || currentPath.value;
  if (!deleteFiles?.length) {
    ElMessage.warning('请选择要删除的文件或文件夹');
    return;
  }

  ElMessageBox.confirm('此操作将永久删除该文件或文件夹, 是否继续?', '提示', {
    buttonSize: 'small',
    cancelButtonText: '取消',
    confirmButtonText: '确定',
    customClass: 'wos-common-confirm',
    type: 'warning',
  })
    .then(() => {
      loading.value = true;
      try {
        const url = `${baseUrl}/folder/delete`;
        fetch(url, {
          body: JSON.stringify(deleteFiles),
          headers: {
            'Content-Type': 'application/json;charset=utf-8',
          },
          method: 'DELETE',
        })
          .then((response) => response.json())
          .then((res) => {
            const { code, msg } = res;
            if (code === 200) {
              const id = expandDictionary.value[deletePath as string];
              if (id) {
                refreshTreeNode([id]);
              }
              // 刷新右侧列表
              reloadNode({ path: deletePath } as CommonTreeProps);
              ElMessage.success({
                message: '删除成功',
                type: 'success',
              });
            } else {
              ElMessage.error(msg || '获取文件夹目录失败');
            }
          });
      } catch (error) {
        console.log(error);
        ElMessage.error('请求发生了错误，请稍后重试');
      } finally {
        loading.value = false;
      }
    })
    .catch(() => {
      console.log('取消');
    });
};

// 重命名
const onRename = (files?: string[], targetPath?: string, oldName?: string) => {
  const renameList = files || checkedList.value;
  const renamePath = targetPath || currentPath.value;
  if (!renameList?.length) {
    ElMessage.warning('请选择要重命名的文件或文件夹');
    return;
  }
  const renameValue = oldName || list.value?.find((m) => m.checked)?.name || '';
  ElMessageBox.prompt('请输入新的文件或文件夹名称', '重命名', {
    buttonSize: 'small',
    cancelButtonText: '取消',
    confirmButtonText: '确定',
    customClass: 'wos-common-confirm',
    draggable: true,
    inputErrorMessage: '无效的名称，请重新输入',
    inputPattern: /^[^"%*/:<>?\\|]+$/,
    inputPlaceholder: '请输入新的文件或文件夹名称',
    inputValue: renameValue,
  })
    .then(({ value }) => {
      loading.value = true;
      try {
        const postData = { files: renameList, name: value };
        const url = `${baseUrl}/folder/rename`;
        fetch(url, {
          body: JSON.stringify(postData),
          headers: {
            'Content-Type': 'application/json;charset=utf-8',
          },
          method: 'POST',
        })
          .then((response) => response.json())
          .then((res) => {
            const { code, msg } = res;
            if (code === 200) {
              const id = expandDictionary.value[renamePath as string];
              if (id) {
                refreshTreeNode([id]);
              }
              // 刷新右侧列表
              reloadNode({ path: renamePath } as CommonTreeProps);
              ElMessage.success({
                message: msg || '重命名成功',
                type: 'success',
              });
            } else {
              ElMessage.error(msg || '获取文件夹目录失败');
            }
          });
      } catch (error) {
        console.log(error);
        ElMessage.error('请求发生了错误，请稍后重试');
      } finally {
        loading.value = false;
      }
    })
    .catch(() => {
      console.log('取消');
    });
};

// 移动到
let moveList: string[];
let movePath: string;
const onMove = (files?: string[], targetPath?: string) => {
  moveList = files || checkedList.value || [];
  movePath = targetPath || currentPath.value || splitChar;
  if (!moveList?.length) {
    ElMessage.warning('请选择要复制的文件或文件夹');
    return;
  }
  moveToRef.value?.open({ canDefaultPath: true });
};
const onMoveToConfirm = (data: { path: string }) => {
  if (moveList?.includes(data.path)) {
    ElMessage.warning('不能移动到自身');
    return;
  }
  loading.value = true;
  try {
    const postData = { files: moveList, target: data.path };
    const url = `${baseUrl}/folder/moveto`;
    fetch(url, {
      body: JSON.stringify(postData),
      headers: {
        'Content-Type': 'application/json;charset=utf-8',
      },
      method: 'POST',
    })
      .then((response) => response.json())
      .then((res) => {
        const { code, msg } = res;
        if (code === 200) {
          const treeId = expandDictionary.value[movePath as string];
          const listId = expandDictionary.value[data.path];
          const ids = [treeId, listId] as TreeKey[];
          refreshTreeNode(ids);
          // 刷新右侧列表
          reloadNode({ path: movePath } as CommonTreeProps);
          ElMessage.success({
            message: msg || '重命名成功',
            type: 'success',
          });
          moveList = [];
        } else {
          ElMessage.error(msg || '获取文件夹目录失败');
        }
      });
  } catch (error) {
    console.log(error);
    ElMessage.error('请求发生了错误，请稍后重试');
  } finally {
    loading.value = false;
  }
};

// 新建文件
const onNewFile = () => {
  const newFilePath = currentPath.value || splitChar;
  ElMessageBox.prompt('请输入文件名称', '新建文件', {
    buttonSize: 'small',
    cancelButtonText: '取消',
    confirmButtonText: '确定',
    customClass: 'wos-common-confirm',
    draggable: true,
    inputErrorMessage: '无效的名称，请重新输入',
    inputPattern: /^[^"%*/:<>?\\|]+$/,
    inputPlaceholder: '请输入文件名称',
    inputValue: '新建 文本文档.txt',
  })
    .then(({ value }) => {
      loading.value = true;
      try {
        const postData = { name: value, path: newFilePath };
        const url = `${baseUrl}/folder/newFile`;
        fetch(url, {
          body: JSON.stringify(postData),
          headers: {
            'Content-Type': 'application/json;charset=utf-8',
          },
          method: 'POST',
        })
          .then((response) => response.json())
          .then((res) => {
            const { code, msg } = res;
            if (code === 200) {
              const id = expandDictionary.value[newFilePath as string];
              if (id) {
                refreshTreeNode([id]);
              }
              // 刷新右侧列表
              reloadNode({ path: newFilePath } as CommonTreeProps);
              ElMessage.success({
                message: msg || '新建文件成功',
                type: 'success',
              });
            } else {
              ElMessage.error(msg || '新建文件失败');
            }
          });
      } catch (error) {
        console.log(error);
        ElMessage.error('请求发生了错误，请稍后重试');
      } finally {
        loading.value = false;
      }
    })
    .catch(() => {
      console.log('取消');
    });
};

// 搜索
const onSearch = () => {
  if (!keyword.value) {
    ElMessage.warning('请输入搜索内容');
    return;
  }
  const path = currentPath.value || splitChar;
  loading.value = true;
  setTimeout(async () => {
    try {
      const url = `${baseUrl}/folder/search`;
      const response = await fetch(url, {
        body: JSON.stringify({ keyword: keyword.value, path }),
        headers: {
          'Content-Type': 'application/json;charset=utf-8',
        },
        method: 'POST',
      });
      const res = await response.json();
      const { code, data, msg } = res;
      if (code === 200 && data) {
        list.value = data as FolderProps[];
        list.value.map((m) => {
          m.icon = formatIcon(m);
          return m;
        });
      } else {
        ElMessage.error(msg || '获取文件夹目录失败');
      }
    } catch (error) {
      console.log(error);
      ElMessage.error('请求发生了错误，请稍后重试');
    } finally {
      loading.value = false;
    }
  }, 100);
};

// 取消搜索
const onResume = () => {
  keyword.value = '';
  const url = `${baseUrl}/folder/resume`;
  fetch(url, {
    method: 'GET',
  })
    .then((response) => response.json())
    .then((_res) => {
      const path = currentPath.value || splitChar;
      reloadNode({ path } as CommonTreeProps);
    });
};

// 选择
const setListCheck = (key: string) => {
  switch (key) {
    case 'check-all': {
      list.value?.map((m) => {
        m.checked = true;
        return m;
      });
      break;
    }
    case 'reverse-check': {
      list.value?.map((m) => {
        m.checked = !m.checked;
        return m;
      });
      break;
    }
    case 'uncheck-all': {
      list.value?.map((m) => {
        m.checked = false;
        return m;
      });
      break;
    }
  }
};

// 列表全选
const onCheckAll = (val: boolean) => {
  setListCheck(val ? 'check-all' : 'uncheck-all');
};

// 双击打开
const onItemDbClick = (item: FolderProps) => {
  const { fullPath, name, type } = item;
  if (type === '2') {
    const node = {
      label: name,
      path: fullPath,
    } as CommonTreeProps;
    reloadNode(node);
    currentPath.value = fullPath;
    saveHistory(fullPath);
  } else {
    switch (item.ext) {
      case '.avi':
      case '.mkv':
      case '.mp4': {
        const app = proxy?.$tool.getAppByKey('video_player');
        appStore.openWithData({ app, data: item });
        break;
      }
      case '.doc': {
        window.open(fullPath);
        break;
      }
      case '.docx': {
        window.open(fullPath);
        break;
      }
      case '.pdf': {
        window.open(fullPath);
        break;
      }
      case '.xls': {
        window.open(fullPath);
        break;
      }
      default: {
        ElMessage.error('暂不支持打开文件'); // 文件
      }
    }
  }
};
// 加载节点
const onLoadNode = (
  node: Node,
  resolve: (data: CommonTreeProps[]) => void,
  reject: () => void,
) => {
  if (node.level === 0) {
    const defaultNode = [
      {
        id: defaultNodeId,
        isLeaf: false,
        label: '我的电脑',
        parentId: '',
        parentPath: splitChar,
        path: splitChar,
        children: [],
      },
    ];
    nextTick(() => {
      currentData.value = defaultNode[0] as CommonTreeProps;
      reloadNode(currentData.value);
    });
    return resolve(defaultNode);
  } else {
    loading.value = true;
    try {
      const url = `${baseUrl}/folder/tree?path=${node.data.path}`;
      fetch(url, {
        method: 'GET',
      })
        .then((response) => response.json())
        .then((res) => {
          const { code, data, msg } = res;
          if (code === 200 && data) {
            data.map((m: CommonTreeProps) => {
              expandDictionary.value[m.path] = m.id;
              m.parentId = node.data.id;
              m.parentPath = node.data.path;
              return m;
            });
            return resolve(data as CommonTreeProps[]);
          } else {
            ElMessage.error(msg || '获取文件夹目录失败');
            return reject();
          }
        });
    } catch (error) {
      console.log(error);
      ElMessage.error('请求发生了错误，请稍后重试');
    } finally {
      loading.value = false;
    }
  }
};

// 注释部分没有考虑到如果右键菜单过高，右键菜单在鼠标点击位置的上方或下方仍然超出可视区域的情况
// 原来的逻辑默认是右键菜单在鼠标点击位置的上方，如果此时右键菜单的高度大于鼠标点击位置距离可视区下面的高度，就会导致右键菜单部分被遮住；之前是直接将右键菜单放在鼠标上方，但是这里也会有问题：右键菜单的高度大于鼠标点击位置距离可视区上面的高度，右键菜单被遮住；所以这里增加了一些判断，如果有这种情况，就以右键菜单的高度的一半放在鼠标点击位置，居中
const clientY = computed(() => {
  const height = document.documentElement.clientHeight;
  const eleHeight = (contextMenuRef.value?.$el.offsetHeight || 200) as number;
  const Y = contextmenuY.value;
  if (eleHeight >= height) {
    if (eleHeight > height) {
      ElMessage.error('右键菜单高度超过可视区域高度,部分会被遮挡');
    }
    return 0;
  } else {
    const getHeight = () => {
      const halfEleHeight = eleHeight / 2;
      if (halfEleHeight < Y && halfEleHeight < height - Y) {
        return Y - halfEleHeight;
      } else if (halfEleHeight > Y) {
        return 0;
      } else if (halfEleHeight > height - Y) {
        return height - eleHeight;
      }
    };
    const _h = Y < eleHeight ? getHeight() : height - (height - Y) - eleHeight;
    return height - Y >= eleHeight ? Y : _h;
  }
});

const contextMenuStyle = computed(() => {
  return {
    left: `${contextmenuX.value}px`,
    top: `${clientY.value}px`,
  };
});
// 文件夹右键 共四个参数，依次为：event、传递给 data 属性的数组中该节点所对应的对象、节点对应的 Node、节点组件本身
const onContextMenu = (e: MouseEvent, data: CommonTreeProps) => {
  e.preventDefault(); // 阻止默认事件
  contextmenuVisible.value = false;
  contextmenuX.value = e.x; // 让右键菜单出现在鼠标右键的位置
  contextmenuY.value = e.y;
  currentData.value = data;
  console.log(currentData.value);
  contextmenuVisible.value = true; // 展示右键菜单
};
const onHideContextMenu = () => {
  contextmenuVisible.value = false;
};
const onContextMenuCommand = (command: string) => {
  if (!currentData.value) return;
  contextmenuVisible.value = false;
  const { id, label, parentPath, path } = currentData.value;
  switch (command) {
    case 'delete': {
      onDelete([path], parentPath);
      break;
    }
    case 'move': {
      onMove([path], parentPath);
      break;
    }
    case 'newFolder': {
      onNewFolder(path);
      break;
    }
    case 'rename': {
      onRename([path], parentPath, label);
      break;
    }
    default: {
      // 刷新
      refreshTreeNode([id]);
      ElMessage.success({ grouping: true, message: '刷新成功' });
      break;
    }
  }
};
// 跳转到指定目录
const onGoto = (path: string) => {
  if (path) {
    currentPath.value = path;
    reloadNode({ path } as CommonTreeProps);
  }
};
onMounted(() => {
  documentElRef.value?.addEventListener('click', onHideContextMenu);
});

onUnmounted(() => {
  expandDictionary.value = {};
  documentElRef.value?.removeEventListener('click', onHideContextMenu);
});

watch(
  () => list.value,
  (val) => {
    checkedList.value = val
      ?.filter((item) => item.checked)
      ?.map((m) => m.fullPath);
  },
  { deep: true, immediate: true },
);

watch(
  () => [sortKey.value, sortOrder.value],
  (val) => {
    switch (val[0]) {
      case 'name': {
        list.value = list.value?.sort((a, b) => {
          return val[1] === 'asc'
            ? a.name.localeCompare(b.name)
            : b.name.localeCompare(a.name);
        });
        break;
      }
      case 'size': {
        list.value = list.value?.sort((a, b) => {
          return val[1] === 'asc' ? a.size - b.size : b.size - a.size;
        });
        break;
      }
      case 'time': {
        list.value = list.value?.sort((a, b) => {
          return val[1] === 'asc'
            ? new Date(a.mtime).getTime() - new Date(b.mtime).getTime()
            : new Date(b.mtime).getTime() - new Date(a.mtime).getTime();
        });
        break;
      }
      case 'type': {
        list.value = list.value?.sort((a, b) => {
          return val[1] === 'asc'
            ? a.type.localeCompare(b.type)
            : b.type.localeCompare(a.type);
        });
        break;
      }
    }
  },
  { immediate: true },
);

watch(
  () => appStore.windowState,
  () => {
    setTimeout(() => {
      if (documentElRef.value) {
        documentHeight.value = documentElRef.value.offsetHeight;
      }
    }, 500);
  },
  { deep: true, immediate: true },
);
</script>

<template>
  <div ref="documentElRef" class="wos-folder">
    <Horizontal
      :default-width="268"
      :max-width="360"
      :min-width="268"
      :now-rect="nowRect"
      @resize="onHorizontalResize"
    >
      <template #aside>
        <div class="side-left">
          <div class="content">
            <ElTree
              ref="treeRef"
              :current-node-key="defaultNodeId"
              :data="treeData"
              :default-expanded-keys="[defaultNodeId]"
              :expand-on-click-node="false"
              :highlight-current="true"
              :load="onLoadNode"
              :props="defaultProps"
              class="folder-custom-tree"
              lazy
              node-key="id"
              size="small"
              @node-click="onNodeClick"
              @node-collapse="onNodeCollaps"
              @node-contextmenu="onContextMenu"
              @node-expand="onNodeExpand"
            >
              <template #default="{ node }">
                <div class="custom-tree-node">
                  <Icon
                    :icon="
                      node.expanded
                        ? `fa6-regular:folder-open`
                        : `fa6-solid:folder`
                    "
                    class="mr-2 size-4"
                  />
                  <span>{{ node.label }}</span>
                </div>
              </template>
            </ElTree>
          </div>
          <div class="footer">共 {{ list?.length || 0 }} 个项目</div>
        </div>
      </template>
      <template #main>
        <div :style="contentRightStyle" class="content-right">
          <div
            :class="[{ 'flex-col items-center gap-2': layoutSize.mw <= 560 }]"
            class="toolbar"
          >
            <div class="toolbar-left">
              <ElButtonGroup size="small">
                <ElButton
                  :disabled="!currentPath || currentPath === splitChar"
                  title="后退"
                >
                  <Icon
                    class="size-4"
                    icon="fa6-solid:angle-left"
                    @click="onHistory(-1)"
                  />
                </ElButton>
                <ElButton
                  :disabled="!currentPath || currentPath === splitChar"
                  title="前进"
                >
                  <Icon
                    class="size-4"
                    icon="fa6-solid:angle-right"
                    @click="onHistory(1)"
                  />
                </ElButton>
              </ElButtonGroup>
              <div class="item" title="上一级">
                <Icon
                  class="size-4"
                  icon="fa6-solid:angle-up"
                  @click="onParent"
                />
              </div>
              <div class="item" title="刷新" @click="onRefresh">
                <Icon class="size-4" icon="mdi:reload" />
              </div>
              <ElDivider direction="vertical" style="height: 24px" />
              <div class="item" title="新建文件夹" @click="() => onNewFolder()">
                <Icon class="size-4" icon="mdi:folder-plus-outline" />
              </div>
              <div class="item" title="新建文件" @click="() => onNewFile()">
                <Icon
                  class="size-4"
                  icon="mdi:file-document-plus-outline"
                />
              </div>
              <div class="item" title="删除" @click="() => onDelete()">
                <Icon class="size-4" icon="mdi:delete-outline" />
              </div>
              <div class="item" title="重命名" @click="() => onRename()">
                <Icon class="size-4" icon="mdi:rename-outline" />
              </div>
              <div class="item" title="移动到" @click="() => onMove()">
                <Icon class="size-4" icon="mdi:move-from-inbox" />
              </div>
              <ElDivider direction="vertical" style="height: 24px" />
              <div class="dropdown-item">
                <Icon
                  :icon="
                    sortOrder === 'asc'
                      ? `mdi:sort-alphabetical-ascending`
                      : `mdi:sort-alphabetical-descending`
                  "
                  class="size-4"
                />
                <ElDropdown trigger="click" @command="setSort">
                  <Icon
                    class="cursor-pointer caret size-3"
                    icon="ep:arrow-down-bold"
                  />
                  <template #dropdown>
                    <ElDropdownMenu>
                      <ElDropdownItem command="name">
                        <Icon
                          :icon="sortKeyState('name')"
                          class="mr-2"
                        />名称
                      </ElDropdownItem>
                      <ElDropdownItem command="size">
                        <Icon :icon="sortKeyState('size')" class="mr-2" />
                        大小
                      </ElDropdownItem>
                      <ElDropdownItem command="type">
                        <Icon
                          :icon="sortKeyState('type')"
                          class="mr-2"
                        />类型
                      </ElDropdownItem>
                      <ElDropdownItem command="time">
                        <Icon
                          :icon="sortKeyState('time')"
                          class="mr-2"
                        />修改时间
                      </ElDropdownItem>
                      <ElDropdownItem command="asc" divided>
                        <Icon
                          :icon="sortOrderState('asc')"
                          class="mr-2"
                        />升序
                      </ElDropdownItem>
                      <ElDropdownItem command="desc">
                        <Icon
                          :icon="sortOrderState('desc')"
                          class="mr-2"
                        />降序
                      </ElDropdownItem>
                    </ElDropdownMenu>
                  </template>
                </ElDropdown>
              </div>
              <ElDivider direction="vertical" style="height: 24px" />
              <div :title="layoutState.title" class="dropdown-item">
                <Icon :icon="layoutState.icon" class="size-4" />
                <ElDropdown trigger="click" @command="setLayout">
                  <Icon
                    class="cursor-pointer caret size-3"
                    icon="ep:arrow-down-bold"
                  />
                  <template #dropdown>
                    <ElDropdownMenu>
                      <ElDropdownItem command="list">
                        <Icon class="mr-2" icon="fa6-solid:list" />详细信息
                      </ElDropdownItem>
                      <ElDropdownItem command="grid-small">
                        <Icon class="mr-2" icon="mdi:grid" />
                        小图标
                      </ElDropdownItem>
                      <ElDropdownItem command="grid-middle">
                        <Icon
                          class="mr-2"
                          icon="mdi:view-grid-outline"
                        />中图标
                      </ElDropdownItem>
                      <ElDropdownItem command="grid-large">
                        <Icon class="mr-2" icon="mdi:grid-large" />大图标
                      </ElDropdownItem>
                    </ElDropdownMenu>
                  </template>
                </ElDropdown>
              </div>
              <ElDivider direction="vertical" style="height: 24px" />
              <div :title="layoutState.title" class="dropdown-item">
                <Icon class="size-4" icon="fa6-solid:list-check" />
                <ElDropdown trigger="click" @command="setListCheck">
                  <Icon
                    class="cursor-pointer caret size-3"
                    icon="ep:arrow-down-bold"
                  />
                  <template #dropdown>
                    <ElDropdownMenu>
                      <ElDropdownItem command="check-all">
                        <Icon
                          class="mr-2"
                          icon="mingcute:list-check-2-line"
                        />全部选择
                      </ElDropdownItem>
                      <ElDropdownItem command="uncheck-all">
                        <Icon
                          class="mr-2"
                          icon="mingcute:list-check-3-line"
                        />全部取消
                      </ElDropdownItem>
                      <ElDropdownItem command="reverse-check">
                        <Icon class="mr-2" icon="ri:list-check-3" />
                        反向选择
                      </ElDropdownItem>
                    </ElDropdownMenu>
                  </template>
                </ElDropdown>
              </div>
            </div>
            <div class="toolbar-right">
              <ElInput
                v-model="keyword"
                clearable
                placeholder="请输入内容进行搜索"
                size="small"
                @clear="onResume"
              >
                <template #append>
                  <ElButton :icon="Search" size="small" @click="onSearch" />
                </template>
              </ElInput>
            </div>
          </div>
          <div class="content">
            <Suspense>
              <template #default>
                <component
                  :is="layoutKey === 'list' ? FolderList : FolderGrid"
                  :key="layoutKey"
                  :layout-key="layoutKey"
                  :list="list"
                  :loading="loading"
                  :sort="sort"
                  :sort-key="sortKey"
                  @check-all="onCheckAll"
                  @item-db-click="onItemDbClick"
                  @resume="onResume"
                  @sort-by="sortBy"
                />
              </template>
              <template #fallback>
                <div class="spinner">加载中...</div>
              </template>
            </Suspense>
          </div>
          <div class="breadcrumb">
            <div
              class="breadcrumb-item last:!rounded-bl-sm last:!rounded-tl-sm"
              @click="onGoto(splitChar)"
            >
              <Icon class="size-4" icon="mdi:folder" />
              <span title="我的电脑">我的电脑</span>
            </div>
            <div
              v-for="(item, index) in breadcrumb"
              :key="index"
              class="breadcrumb-item"
              @click="onGoto(item.path)"
            >
              <span :title="item.name">{{ item.name }}</span>
            </div>
          </div>
        </div>
      </template>
    </Horizontal>
    <Teleport to="body">
      <ContextMenu
        v-show="contextmenuVisible"
        ref="contextMenuRef"
        :style="contextMenuStyle"
        class="absolute"
        v-click-outside="onHideContextMenu"
        @command="onContextMenuCommand"
      />
    </Teleport>
    <Teleport to="body">
      <MoveTo ref="moveToRef" @confirm="onMoveToConfirm" />
    </Teleport>
  </div>
</template>

<style lang="scss">
@import url('./css/folder.scss');
</style>
